/*
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *      
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *      
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 */

#ifndef _LEXICAL_H
#define _LEXICAL_H

#include <iostream>
#include <string>
#include <cctype>
#include <sstream>
#include <cstring>
#include <exception>

#include "reader.h"

#define CHARACTER_CR '\r'
#define CHARACTER_LF '\n'

#define RESERVE_OBJECT "OBJECT"
#define RESERVE_OBJECT_LEN 6 

#define RESERVE_ALTER_OBJECT_START "START"
#define RESERVE_ALTER_OBJECT_START_LEN 5
#define RESERVE_ALTER_OBJECT_END "END"
#define RESERVE_ALTER_OBJECT_END_LEN 3

//! Lexikální analýza.
/*!
 * Přebírá znaky z readeru a provádí lexikální analýzu. Výstupem jsou
 * tokeny, nebo také vyjímka při neočekávaném symbolu.
*/
class lexical{

	public:
		//! Vyjímka neplatného tokenu.
		/*!
		* Vyjímka nastane když se na vstupnu objeví neočekávaný znak.
		* Znak, který nepatří do jazyka g-codu.
		*/
		class invalidToken: public std::exception{
			
			private:
				unsigned line;
			
			public:
				invalidToken(unsigned) throw();
				virtual const char *what() const throw();
				//! Vrací řádek neplatného znaku.
				/*!
				* Lexikální analyzátor zaznamenává řádky, na kterých přímá znaky.
				* Potom je možné dozvědět se, na jakém řádku došlo k chybě.
				*/
				virtual unsigned getLine() const;
			
		};
		
		//! Výstupní jednotka analyzátoru.
		/*!
		* Token obsahuje výčtovou typ všech symbolů, které mohou být
		* analyzovány. Tento typ je dále používám v další stupních analýzy.
		* Kromě symbolu obsahuje token také lexém.
		*/
		class token{
			
			//! Výčet všech symbolů.
			/*!
			* Všechny symboly, které mohou být rolišovány dále syntaktickým
			* analyzátorem, nebo používány v jiných typech. Mezi symboly se objevují
			* i takové, jako konec souboru, nebo chybný token.
			*/
			public:
				enum symbols{
						//! Interpolace a jiné funkce.
						GO_G, 
						//! Rychlost posuvu.
						FEED_F, 
						//! Rychlost otáčení vřetena.
						SPINDLE_S, 
						//! Číslo řádky.
						LINE_N, 
						//! Funkce definované strojem.
						MACHINE_M, 
						//! Parametr kruhové interpolace.
						RADIUS_R, 
						//! Parametr interpolace, osa x.
						AXIS_X,
						//! Parametr interpolace, osa y.
						AXIS_Y,
						//! Parametr interpolace, osa z.
						AXIS_Z,
						//! Parametr kruhové interpolace, střed, osa x.
						ARC_I,
						//! Parametr kruhové interpolace, střed, osa y.
						ARC_J,
						//! Parametr kruhové interpolace, střed, osa z.
						ARC_K,
						//! Běžný kometář.
						COMMENT,
						//! Komentář s označením začátku bloku.
						OBJECT_START,
						//! Komentář s označením konce bloku.
						OBJECT_END,
                                                //! Alternative start
                                                OBJECT_ALTER_START,
                                                //! Alternative end
                                                OBJECT_ALTER_END,
						//! Změna nástroje.
						TOOL_T,
						//! Označení podprogramu.
						SUBROUTINE_O,
						//! Volání podprogramu.
						CALL_P,
						//! Konec souboru.
						END,
						//! Nastala chyba, neplatný token.
                                                ERR
						};
						
				symbols symbol;
				//! Lexem.
				/*!
				* Obsahuje lexem daného symbolu. Číselné hodnoty jsou reprezentovány
				* řetězcem, je nutné potom dalších úprav.
				*/
				std::string lexem;
				//! Řádek tokenu.
				/*!
				* Obsahuje informaci řádku v souboru, na kterém se daný token 
				* vyskytuje.
				*/
				unsigned line;
				unsigned long startOffset, endOffset;
		};

	private:
	
		//! Ukazatel na reader.
		/*!
		* \param rd Reader vracející znaky ze souboru.
		*/
		reader *fileReader;
		//! Aktuální řádek.
		unsigned line;
		
		//! Načte řetězec.
		/*!
		 * Jde v podstatě o reinplementaci metody z readeru reader::getString.
		* \param str Použitý řetězec.
		* \param num Délka řetězce.
		*/
		std::string &getString(std::string &, unsigned);
		//! Porovná řetězec.
		/*!
		* Porovná řetězec se znaky, které vrací reader.
		*/
        bool compare(const char *, unsigned, unsigned *);
		//! Vrací znak z readeru.
		/*!
		* Reimplementace metody z readeru reader::getChar.
		*/
		int getCharacter() throw(std::ifstream::failure);
		//! Posouvá se o znak zpět.
		/*!
		* Reimplementace metody z readeru reader::getBack.
		* Může vzniknout vyjímka spojená s čtením ze souboru.
		*/
		void characterBack() throw(reader::bufferOverflow);
		//! Vloží jméno do tokenu.
		/*!
		* Parsuje jméno z tagu bloku (#OBJECT jméno) a (#!OBJECT jméno).
		* Ve jménu nejsou povolené mezery, zpracovává alfanumerické znaky
		* plus znak podtržítka([a-zA-Z0-9_]+).
		* Vyhazuje vyjímku reader::bufferOverflow, což je vyjímka spojená
		* s přetečením bufferu readeru. std::ifstream::failure chyba spojená
		* s čtením souboru.
		*/
		token &getName(token &) throw(reader::bufferOverflow, std::ifstream::failure);
		//! Vloží realné číslo do tokenu.
		/*!
		* Vyhovují všechna realný čísla. Používá se desetiná tečka ne čárka
		* a zápis s exponentem není povolený([0-9]+(.[0-9]*)?]). 
		* Vyhazuje vyjímku reader::bufferOverflow, což je vyjímka spojená
		* s přetečením bufferu readeru. std::ifstream::failure chyba spojená
		* s čtením souboru.
		*/
		token &getRealNumber(token &) throw(reader::bufferOverflow, std::ifstream::failure);
		//! Vloží celé číslo.
		/*!
		* Vyhovují všechna celá čísla([0-9]+). 
		* Vyhazuje vyjímku reader::bufferOverflow, což je vyjímka spojená
		* s přetečením bufferu readeru. std::ifstream::failure chyba spojená
		* s čtením souboru.
		*/
		token &getIntegerNumber(token &) throw(reader::bufferOverflow, std::ifstream::failure);
		//! Vloží obsah komentáře do tokenu.
		/*!
		* Komentář se rozděluje na dva druhy. Běžné komentáře, které obsahují libovolný text a
		* komentáře, které označují bloky v kódu. Bloky v kódu jsou ohraničené a mají své
		* jméno. Příklad takovéhoto bloku (#OBJECT jmeno)... (#!OBJECT jmeno).
		* Jak je vidě tvoří ho párové značky, které blok uzavírají. Jméno bloku musí být
		* v rámci souboru jednoznačné.
		* Vyhazuje vyjímku reader::bufferOverflow, což je vyjímka spojená
		* s přetečením bufferu readeru. std::ifstream::failure chyba spojená
		* s čtením souboru.
		*/
		token &parseComment(token &) throw(reader::bufferOverflow, std::ifstream::failure);
        token &parseAlterComment(token &) throw(reader::bufferOverflow, std::ifstream::failure);
        //! Vrací délku přečteného.
		/*!
		* Reinplementace reader::getLength().
		*/
		unsigned long getLength() const;
		
	public:
		//! Konstruktor analyzátoru.
		/*!
		* 
		*/
		lexical(reader *);
		
		//! Vrací token.
		/*!
		* Odstraňuje všechny přebytečné znaky jako např. mezery, tabulátory, ... a vrací token.
		* Při dosažení konce souboru, token obsahuje symbol END. Při vracení tokenu může být vyhozeno
		* několik vyjímek. Vyjímka invalidToken je vyhozena pokud na vstup analyzátoru dojde neplatný
		* symbol. Vyjímka bufferOverflow nastane při přetečení bufferu u readeru (Tato vyjímka 
		* je čistě teoretická, při správném použití readeru by nikdy neměla nastat.). std::ifstream::failure
		* souvisí s chybou při čtení souboru.
		*/
		token &getToken(token &) throw(invalidToken, reader::bufferOverflow, std::ifstream::failure);
	
};

#endif
